#include "litelog.h"
#include "liteutils.h"

namespace
{
	const char* const levelStr[3] =
	{
		"INFO",
		"WARNING",
		"ERROR"
	};
}

LiteLog::LiteLog()
{

}

LiteLog::~LiteLog()
{
	if (!m_bQuit)
		uninit();
}


LiteLog* LiteLog::instance()
{
	static LiteLog ins;
	return &ins;
}

bool LiteLog::init(std::string logFileName, LiteUtils::TimeLevelType timeLevel)
{
	if (m_bInit)
		return false;

	m_timeLevel = timeLevel;
	m_queue = std::make_unique<LiteSafeInfoQueue>();
	if (!m_queue)
		return false;

	m_fout.open(logFileName, std::ios_base::app | std::ios_base::out);
	if (!m_fout.is_open())
		return false;

	m_coreThread = std::make_unique<std::thread>(&LiteLog::writeToNative, std::ref(*this));
	if (!m_coreThread)
		return false;
	
	m_bInit = true;
	return true;
}

void LiteLog::uninit()
{
	m_bQuit = true;
	if (m_queue)
	{
		m_queue->destory();
	}

	if (m_coreThread && m_coreThread->joinable())
	{
		m_coreThread->join();
	}
}

LiteUtils::TimeLevelType LiteLog::logTimeLevel() const
{
	return m_timeLevel;
}

bool LiteLog::isInited() const
{
	return m_bInit;
}

void LiteLog::setDebugPrint(bool bDebugPrint)
{
	m_bCout = bDebugPrint;
}

void LiteLog::writeInfo(const std::string& moduleName, const std::string& functionName, const std::string& info)
{
	std::string strInfo = LiteUtils::stringSprintf("[%s] [%s] [%s]", moduleName.c_str(), functionName.c_str(), info.c_str());
	collectLog(LogLevel::INFO, std::move(strInfo));
}

void LiteLog::writeInfo(const std::string&& moduleName, const std::string&& functionName, const std::string&& info)
{
	std::string strInfo = LiteUtils::stringSprintf("[%s] [%s] [%s]", moduleName.c_str(), functionName.c_str(), info.c_str());
	collectLog(LogLevel::INFO, std::move(strInfo));
}

void LiteLog::writeWarning(const std::string& moduleName, const std::string& functionName, const std::string& info)
{
	std::string strInfo = LiteUtils::stringSprintf("[%s] [%s] [%s]", moduleName.c_str(), functionName.c_str(), info.c_str());
	collectLog(LogLevel::WARNING, std::move(strInfo));
}

void LiteLog::writeWarning(const std::string&& moduleName, const std::string&& functionName, const std::string&& info)
{
	std::string strInfo = LiteUtils::stringSprintf("[%s] [%s] [%s]", moduleName.c_str(), functionName.c_str(), info.c_str());
	collectLog(LogLevel::WARNING, std::move(strInfo));
}

void LiteLog::writeError(const std::string& moduleName, const std::string& functionName, const std::string& info)
{
	std::string strInfo = LiteUtils::stringSprintf("[%s] [%s] [%s]", moduleName.c_str(), functionName.c_str(), info.c_str());
	collectLog(LogLevel::ERROR, std::move(strInfo));
}

void LiteLog::writeError(const std::string&& moduleName, const std::string&& functionName, const std::string&& info)
{
	std::string strInfo = LiteUtils::stringSprintf("[%s] [%s] [%s]", moduleName.c_str(), functionName.c_str(), info.c_str());
	collectLog(LogLevel::ERROR, std::move(strInfo));
}


void LiteLog::collectLog(LogLevel level, const std::string&& logInfo)
{
	std::string time = LiteUtils::getCurrentTime(logTimeLevel());
	std::string strLog = LiteUtils::stringSprintf("%s  %s  %s\n", time.c_str(), levelStr[static_cast<int>(level)], logInfo.c_str());
	
	if (m_bCout)
		std::cout << strLog;

	m_queue->push(std::move(strLog));
}

void LiteLog::writeToNative()
{
	while (!m_bQuit)
	{
		if (!m_queue)
			return;

		std::string strLog = m_queue->pop();
		if (strLog.size() <= 0)
			continue;

		m_fout << strLog;
		m_fout.flush();
	}
}